package com.warm.orm.config;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.solon.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.solon.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.solon.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.solon.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.solon.plugins.inner.TenantLineInnerInterceptor;
import com.warm.cache.utils.CacheUtil;
import com.warm.core.constant.CacheConstants;
import com.warm.core.utils.StringUtils;
import com.warm.orm.handler.HhTenantLineHandler;
import com.warm.orm.handler.InjectionMetaObjectHandler;
import com.warm.tenant.helper.TenantHelper;
import com.warm.tenant.properties.TenantProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.solon.annotation.Db;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.dynamicds.DynamicDataSource;

import javax.sql.DataSource;

/**
 * MybatisPlus配置
 *
 * @author warm
 */
@Configuration
@Slf4j
public class MybatisConfig {


    /**
     * 动态数据源（直接注入）
     *
     * @param dataSource 数据源
     * @return
     */
    @Bean(name = "db1", typed = true)
    public DataSource dbUser(@Inject("${dataSource.db1}") DynamicDataSource dataSource) {
        log.info("orm-plus 加载数据源 ....");
        return dataSource;
    }

    /**
     * MybatisPlus全局配置
     *
     * @param globalConfig 数据源
     */
    @Bean
    public void db1_cfg(@Db("db1") MybatisConfiguration cfg,
                        @Db("db1") GlobalConfig globalConfig,
                        @Inject("${tenant}") TenantProperties tenantProperties) {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        if (tenantProperties.getEnable()) {
            // 多租户插件 必须放到第一位
            interceptor.addInnerInterceptor(tenantLineInnerInterceptor(tenantProperties));
            // 多租户情况下，需要重新赋值key
            CacheUtil.setFunction(this::getKey);
        }
        // 分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor());
        // 乐观锁插件
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
        // 阻断插件
        interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
        cfg.addInterceptor(interceptor);
        globalConfig.setIdentifierGenerator(idGenerator());
    }

    /**
     * 元对象字段填充控制器
     */
    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new InjectionMetaObjectHandler();
    }

    /**
     * 分页插件，自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html
     */
    public PaginationInnerInterceptor paginationInnerInterceptor()
    {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 设置数据库类型为mysql
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        // 设置最大单页限制数量，默认 500 条，-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        // 分页合理化
        paginationInnerInterceptor.setOverflow(true);
        return paginationInnerInterceptor;
    }

    /**
     * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
     */
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor()
    {
        return new OptimisticLockerInnerInterceptor();
    }

    /**
     * 使用网卡信息绑定雪花生成器
     * 防止集群雪花ID重复
     */
    public IdentifierGenerator idGenerator() {
        return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
    }

    /**
     * 如果是对全表的删除或更新操作，就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html
     */
    public BlockAttackInnerInterceptor blockAttackInnerInterceptor()
    {
        return new BlockAttackInnerInterceptor();
    }

    /**
     * 多租户插件
     */
    public TenantLineInnerInterceptor tenantLineInnerInterceptor(@Inject("${tenant}") TenantProperties tenantProperties) {
        return new TenantLineInnerInterceptor(new HhTenantLineHandler(tenantProperties));
    }

    public String getKey(String key) {
        if (!StpUtil.isLogin()) {
            return key;
        }
        if (StringUtils.startsWith(key, CacheConstants.GLOBAL_REDIS_KEY)) {
            return key;
        }
        String tenantId = TenantHelper.getTenantId();
        return tenantId + ":" + key;
    }
}
